package structures;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTextArea;

import Events.AsteroidField;
import Events.EndGameEvent;
import Events.PathEvent;
import Events.PlanetEvent;
import Events.PlanetType;
import Events.StartGameEvent;
import Events.random.AlienEncounter;
import Events.random.EngineTrouble;
import Events.random.SandwichHerd;

/**
 * Path: Displays general game progress - game log, options to change pace/rations
 *
 */
@SuppressWarnings("serial")
public class Path extends JPanel implements ActionListener, Runnable, Serializable{
    
	//More important stuff
    private Spaceship ship;
    private double nextLocal;
    private int peind;
    private boolean paused;
    private List<PathEvent> events;
    private int hypergens; //unit of time
    
    //GUI stuff
    JFrame frame;
    private JMenuBar menubar;
    private JMenu menu;
    private JMenuItem saveMenu, loadMenu, exitMenu;
    private JButton btnPause, btnMove, btnMoveOnce;
    private JTextArea log;
    private JScrollPane scroll;
    private static Image image;
    private static Image inventoryBtn;
    private static Image shipBtn;
    private static Image foodBtn;
    private static Image statusBtn;
    private JButton btnStatus;
    private JButton btnInventory;
    private JButton btnPace;
    private JButton btnRations;
    private JSlider slider;
    static final int FPS_MIN = 0;
    static final int FPS_MAX = 100;
    static final int FPS_INIT = 0;

    
    /**
     * Path Constructor
     * @param ship
     */
    public Path(Spaceship ship, JFrame frame) {
        this.ship = ship;
        this.paused = false;
        //path = new Thread(this);
        nextLocal = 0;
        peind = 0;
        events = new ArrayList<PathEvent>();
        StartGameEvent sge = new StartGameEvent();
        events.add(sge);
        
        events.add(new PlanetEvent("Zero", "Zero", PlanetType.DESERT, 5));
        events.add(new PlanetEvent("One", "One", PlanetType.JUNGLE, 15));
        events.add(new AsteroidField("Asteroid Field", "Asteroid Field", 20));
        events.add(new AsteroidField("Asteroid Field2", "Asteroid Field", 41));
        events.add(new AsteroidField("Asteroid Field3", "Asteroid Field", 65));
        
        events.add(new EndGameEvent("Win", "YOU WON!", FPS_MAX));
        
        hypergens = 0;
        
        //phases[0].setPreferredSize(new Dimension(600,550));
        
        this.setPreferredSize(new Dimension(700, 700));
        
        try
        {
          image = javax.imageio.ImageIO.read(new java.net.URL(getClass().getResource("space.gif"), "space.gif"));
          inventoryBtn = javax.imageio.ImageIO.read(new java.net.URL(getClass().getResource("status.gif"), "status.gif"));
          shipBtn = javax.imageio.ImageIO.read(new java.net.URL(getClass().getResource("ship.gif"), "ship.gif"));
          foodBtn = javax.imageio.ImageIO.read(new java.net.URL(getClass().getResource("sandwich.gif"), "sandwich.gif"));
          statusBtn = javax.imageio.ImageIO.read(new java.net.URL(getClass().getResource("players.gif"), "players.gif"));
        }
        catch (Exception e) { /*handled in paintComponent()*/ }
        
        // Menu Stuff
        this.frame = frame;
        
        menubar = new JMenuBar();
        menu = new JMenu("File");
        menubar.add(menu);
        
        saveMenu = new JMenuItem("Save");
        saveMenu.addActionListener(this);
        menu.add(saveMenu);
        
        loadMenu = new JMenuItem("Load");
        loadMenu.addActionListener(this);
        menu.add(loadMenu); 
        
        exitMenu = new JMenuItem("Exit Game");
        exitMenu.addActionListener(this);
        menu.add(exitMenu);
        
        frame.setJMenuBar(menubar);
        
        // Button Stuff
        btnMove = new JButton("Continue");
        btnMove.setBounds(125, 259, 106, 23);
        btnMove.addActionListener(this);
        setLayout(null);
        add(btnMove);
        
        btnMoveOnce = new JButton("Move One Turn");
        btnMoveOnce.setBounds(234, 259, 161, 23);
        btnMoveOnce.addActionListener(this);
        add(btnMoveOnce);
        
        btnPause = new JButton("Time-Out");  
        btnPause.setBounds(398, 259, 106, 23);
        btnPause.addActionListener(this); 
        add(btnPause);
        
        log = new JTextArea("This is the log of your game progress:\n", 10, 20);
        log.setEditable(false);
        log.setLineWrap(true);
        log.setWrapStyleWord(true);
        scroll = new JScrollPane(log); // add text box to scroll pane
        scroll.setBounds(125, 286, 378, 391);
        scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        add(scroll);
        
        btnStatus = new JButton("Status");
        btnStatus.setBounds(5, 107, 90, 23);
        btnStatus.addActionListener(this);
        btnStatus.setEnabled(false);
        add(btnStatus);
        
        btnInventory = new JButton("Inventory");
        btnInventory.addActionListener(this);
        btnInventory.setBounds(5, 237, 90, 23);
        btnInventory.setEnabled(false);
        add(btnInventory);
        
        btnPace = new JButton("Pace");
        btnPace.addActionListener(this);
        btnPace.setBounds(5, 367, 90, 23);
        btnPace.setEnabled(false);
        add(btnPace);
        
        btnRations = new JButton("Rations");
        btnRations.addActionListener(this);
        btnRations.setBounds(5, 497, 90, 23);
        btnRations.setEnabled(false);
        add(btnRations);
        
        slider = new JSlider(JSlider.VERTICAL,FPS_MIN, FPS_MAX, FPS_INIT);
        slider.setEnabled(false);
        slider.setBounds(567, 31, 90, 658);
        slider.setMajorTickSpacing(25);
        slider.setMinorTickSpacing(1);
        slider.setPaintTicks(true);
        slider.setPaintLabels(true);
        add(slider);
        
        JLabel lblDistance = new JLabel("Distance Travelled");
        lblDistance.setBounds(555, 11, 106, 14);
        add(lblDistance);
        
    }
    
    @Override
    protected void paintComponent(Graphics g)
    {
      super.paintComponent(g); 
      if (image != null)
        g.drawImage(image, 125,10,378,243,this);
      	g.drawImage(statusBtn, 5,11,90,90,this);
      	g.drawImage(inventoryBtn, 5,140,90,90,this);
      	g.drawImage(shipBtn, 5,270,90,90,this);
      	g.drawImage(foodBtn, 5,400,90,90,this);
    }
    

    /**
     * Continually runs, until an event occurs, Spaceship updates
     */
    public void run() {
        while(!paused) {
            updateSpaceship();
            try {
                Thread.sleep(2000); //this fucks up when you pause. 
                					//if you don't wait 2s, you end up having 2 threads of run
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    /**
     * Updates the Spaceship for one turn, telling it to move and to use its rations
     * Also, appends text to the log
     */
    public void updateSpaceship() {
    	hypergens++;
        ship.move();
        if(!checkEvent()){
        	Random rnd = new Random();
        	int chance = rnd.nextInt(100);
        	if(chance < 50){
        		(new SandwichHerd()).execute(ship, log, this);
        	}
        	else if(chance < 70){
        		(new AlienEncounter()).execute(ship, log, this);
        	}
        	else if(chance < 90){
        		(new EngineTrouble()).execute(ship, log, this);
        	}
        }
        slider.setValue((int) ship.getLocation());
        if (ship.getLocation() != 0 & ship.getLocation() != ship.getLastLocation()) {
            log.append("\n" + ship.getUpdateText() + "\n");
            ship.useRations();
            log.append(ship.getUpdateText() + "\n");
            ship.useWater();
            log.append(ship.getUpdateText() + "\n");
            log.setCaretPosition(log.getDocument().getLength()); // moves view to just added lines
        }
    }
    
    /** 
     * Check for event on Path, namely planet
     */
    public boolean checkEvent() {
        if (ship.getLocation() >= nextLocal){            
            PathEvent pe = events.get(peind);
            log.append("\n" + "You arrived at " + pe.getName() + ".\n");
            pe.execute(ship, log);
            JFrame f = new JFrame();
            f.add(pe);
            f.pack();
            f.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
            f.setAlwaysOnTop(true);
            this.getParent().getParent().getParent().getParent().setVisible(false);
            f.setVisible(true);
            while(pe.isRunning()){
                try{
                    Thread.sleep(100);
                } catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
            //System.out.println("Out");
            f.setVisible(false);
            this.getParent().getParent().getParent().getParent().setVisible(true);
            
            // set spaceship location to the location of this event
            if (pe instanceof PlanetEvent) {
                ship.setLocation(nextLocal);
                ship.updateUpdateText();
            }
            else if (pe instanceof AsteroidField) {
                if (ship.getLocation() < nextLocal + ((AsteroidField)pe).getDistance()) {
                    ship.setLocation(nextLocal + ((AsteroidField)pe).getDistance());
                }
                ship.updateUpdateText();
            }
                
            try {
                nextLocal = events.get(++peind).getLocal();
            }
            catch (IndexOutOfBoundsException e) {
                nextLocal = Integer.MAX_VALUE;
            }
            return true;
        }
        return false;
    }
    
    /**
     * Recognizes actions performed on the buttons and reacts based on which button is pressed
     */
    //@Override
    public void actionPerformed(ActionEvent e) { 
    	Pace newPace = Pace.STOPPED;
        if (e.getSource() == btnMove) {
            Thread path = new Thread(this);
            path.start();
            btnMove.setEnabled(false);
            btnMoveOnce.setEnabled(false);
        }
        else if (e.getSource() == btnMoveOnce) {
            paused = true;
            updateSpaceship();
        }
        else if (e.getSource() == btnPause) {
            if (!paused) {
                paused = true;
                btnPause.setText("Time-In");
                btnMove.setEnabled(false);
                btnMoveOnce.setEnabled(false);
                btnStatus.setEnabled(true);
                btnRations.setEnabled(true);
                btnInventory.setEnabled(true);
                btnPace.setEnabled(true);
            }
            else {
                paused = false;
                btnPause.setText("Time-Out");
                btnMove.setEnabled(true);
                btnMoveOnce.setEnabled(true);
                btnStatus.setEnabled(false);
                btnRations.setEnabled(false);
                btnInventory.setEnabled(false);
                btnPace.setEnabled(false);
            }
        }
        else if (e.getSource() == btnStatus) {
        	JOptionPane.showMessageDialog(this, "<html>Party Status</html>", "Party Status", JOptionPane.PLAIN_MESSAGE);
        }  
        else if (e.getSource() == btnInventory) {
        	JOptionPane.showMessageDialog(this, "<html>Spaceship Inventory:<br></html>" + "\n\n" + ship.getInventory(), "Inventory", JOptionPane.PLAIN_MESSAGE);
        	System.out.println(ship.getInventory());
        }
        else if (e.getSource() == btnPace) {
        	Object[] selectionValues = { "Travel Slow", "Travel Cautiously", "Travel Normally", "Travel Quickly"};
            String initialSelection = "Travel Slow";
            Object selection = JOptionPane.showInputDialog(null, "Spaceship Pace",
                "Spaceship Pace", JOptionPane.QUESTION_MESSAGE, null, selectionValues, initialSelection);
            if (selection == "Travel Slow") {
            	newPace = Pace.SLOW;
            }
            else if (selection == "Travel Cautiously"){
            	newPace = Pace.CAUTIOUSLY;
            }
            else if (selection == "Travel Normally"){
            	newPace = Pace.NORMALLY;
            }
            else if (selection == "Travel Quickly"){
            	newPace = Pace.QUICKLY;
            }
            else {
            	newPace = ship.getPace();
            }
            //System.out.println(newPace);
            ship.setPace(newPace);
            //System.out.println(ship.getPace());
        }
        else if (e.getSource() == btnRations) {
            Object[] selectionValues = {"Eat Poorly", "Eat Sufficiently", "Eat Well", "Overindulge"};
            Object selection = JOptionPane.showInputDialog(null, "Spaceship Rations", "Set Spaceship Rations:",
                    JOptionPane.QUESTION_MESSAGE, null, selectionValues, "Eat Well");
            if (selection.equals("Eat Poorly"))
                ship.setRations(Rations.POORLY);
            else if (selection.equals("Eat Sufficiently"))
                ship.setRations(Rations.SUFFICIENTLY);
            else if (selection.equals("Eat Well"))
                ship.setRations(Rations.WELL);
            else if (selection.equals("Overindulge"))
                ship.setRations(Rations.OVERINDULGE);
        }
        else if (e.getSource() == saveMenu) {
            this.save();
        }
        else if (e.getSource() == loadMenu) {
            this.load();
        }
        else if (e.getSource() == exitMenu) {
            System.exit(0);
        }
    }
    
    /**
     * Save the path via Serialization
     */
    private void save() {
        JFileChooser chooser = new JFileChooser();
        int status = chooser.showSaveDialog(null);
        if (status == JFileChooser.APPROVE_OPTION) {
            try {
                String filename = chooser.getSelectedFile().getAbsolutePath();
                //System.out.println(filename);
                ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filename));              
                out.writeObject(this);
            } catch (FileNotFoundException e) {
                System.err.println(e.getMessage() + e.getStackTrace());
            } catch (IOException e) {
                System.err.println(e.getMessage() + e.getStackTrace());
            }
        }            
    }
    
    /**
     * Load saved path via Serialization
     */
    private void load() {
        JFileChooser chooser = new JFileChooser();
        int status = chooser.showOpenDialog(null);
        Path p = null;
        if (status == JFileChooser.APPROVE_OPTION) {
            try {
                String filename = chooser.getSelectedFile().getAbsolutePath();
                ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename));
                Object o = in.readObject();
                if (o instanceof Path) {
                    p = (Path) o;
                }
            } catch (FileNotFoundException e) {
                System.err.println(e.getMessage());
                JOptionPane.showMessageDialog(this, chooser.getSelectedFile().getName() + " does not exist", "Uh Oh!!", JOptionPane.INFORMATION_MESSAGE);
            } catch (IOException e) {
                System.err.println(e.getMessage());
            } catch (ClassNotFoundException e) {
                System.err.println(e.getMessage());
            }
        }
        
        if (p != null) {
            this.setVisible(false);
            frame.remove(this);
            frame.add(p);
            p.paused = false;
            p.setVisible(true);
            frame.setVisible(true);
        }
    }

}
